﻿//==============================================================================
// File: AssociationSync.cs
// Created: 2010-05-10
// Author: Piotr Włodek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with WpfRcp. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using System.Collections.Specialized;
using WpfRcp.PropertyModel.Properties;

namespace WpfRcp.PropertyModel
{
    /// <summary>
    /// Provides functionality of synchronization between properties' values.
    /// Mechanism supports OneToMany, ManyToOne and ManyToMany associations. To provide full bidirectional synchronization,
    /// <see cref="AssociationSync"/> needs to be correctly set on both sides on association, i.e. on both <see cref="MetaProperty"/> fields.
    /// </summary>
    [Serializable]
    public class AssociationSync
    {
        /// <summary>
        /// Creates new association.
        /// </summary>
        /// <param name="metaProperty">MetaProperty which to synchronize with.</param>
        /// <param name="syncType">One of synchronization types.</param>
        public AssociationSync(MetaProperty metaProperty, SyncType syncType)
        {
            if (metaProperty == null)
                throw new ArgumentNullException("metaProperty", Resources.MetaPropertyIsNull);

            if (metaProperty.PropertyKind != MetaPropertyKind.Permanent)
                throw new ArgumentException(Resources.SynchronizationOnNonPermanentProperty, "metaProperty");

            MetaProperty = metaProperty;
            SyncType = syncType;
        }

        /// <summary>
        /// Gets value of MetaProperty with which syncrhonization is performed.
        /// </summary>
        public MetaProperty MetaProperty { get; private set; }

        /// <summary>
        /// Gets SyncType.
        /// </summary>
        public SyncType SyncType { get; private set; }

        internal void ValidateAssociation(object oldValue, object newValue)
        {
            switch (SyncType)
            {
                case SyncType.ManyToOne:
                    if ((oldValue != null && !(oldValue is PropertyContainer)) || (newValue != null && !(newValue is PropertyContainer)))
                        throw new ArgumentException("To use ManyToOne synchronization this property must be of type that derives from PropertyContainer.");
                    break;
                case SyncType.OneToMany:
                    if ((oldValue != null && !(oldValue is INotifyCollectionChanged)) || (newValue != null && !(newValue is INotifyCollectionChanged)))
                        throw new ArgumentException("To use OneToMany synchronization this property must be of type that implements INotifyCollectionChanged.");
                    break;
                case SyncType.ManyToMany:
                    if ((oldValue != null && !(oldValue is INotifyCollectionChanged)) || (newValue != null && !(newValue is INotifyCollectionChanged)))
                        throw new ArgumentException("To use ManyToMany synchronization this property must be of type that implements INotifyCollectionChanged.");
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }

    /// <summary>
    /// Defines synchronization type.
    /// </summary>
    [Serializable]
    public enum SyncType
    {
        /// <summary>
        /// Many to one synchronization.
        /// </summary>
        ManyToOne,

        /// <summary>
        /// One to many synchronization.
        /// </summary>
        OneToMany,

        /// <summary>
        /// Many to many synchronization.
        /// </summary>
        ManyToMany
    }
}
